struct cdq_cdq_t{ // 三维偏序，CDQ套CDQ

using value_t = int;
using cmp_t = function<bool(value_t, value_t)>;

struct _t{
    value_t x;
	value_t y;
	value_t z;
	int flag;
	int pos;
};

int N;
cmp_t cmp1, cmp2, cmp3;
vector<_t> A, B, C;

cdq_cdq_t() = default;

void setCmp(cmp_t c1, cmp_t c2, cmp_t c3) {
	cmp1 = c1; cmp2 = c2; cmp3 = c3;
}

void input(){
	cin >> N;
	A.assign(N, {});
	B.assign(N, {});
	C.assign(N, {});
	int k = 0;
	for(auto & p : A) {
		cin >> p.x >> p.y >> p.z;
		p.pos = k++;
		p.flag = 0;
	}
	return;
}

vector<int> Ans;

void run(){
    Ans.assign(N, 0);
	sort(A.begin(), A.end(), [this](const _t & a, const _t & b){
		if(a.x != b.x) return this->cmp1(a.x, b.x);
		if(a.y != b.y) return this->cmp2(a.y, b.y);
		if(a.z != b.z) return this->cmp3(a.z, b.z);
		if(a.pos != b.pos) return a.pos < b.pos;
		return a.flag < b.flag;
	});
	CDQ(0, N - 1);
}


void CDQ(int s, int e){
	if(s >= e) return;

	int mid = (s + e) >> 1;
	CDQ(s, mid); CDQ(mid + 1, e);

	int u = s, v = mid + 1, t = s;
	while(u <= mid and v <= e){
        if(cmp2(A[u].y, A[v].y)){
			B[t] = A[u++];
			B[t++].flag = 1;
		}else{
			B[t] = A[v++];
			B[t++].flag = 0;
		}
	}
	while(u <= mid){
        B[t] = A[u++];
		B[t++].flag = 1;
	}
	while(v <= e){
        B[t] = A[v++];
		B[t++].flag = 0;
	}
	copy(B.begin() + s, B.begin() + e + 1, A.begin() + s);
	CDQ2(s, e);
	return;
}

void CDQ2(int s, int e){
	if(s >= e) return;

	int mid = (s + e) >> 1;
	CDQ2(s, mid); CDQ2(mid + 1, e);
	 
    int cnt = 0; // 记录y符合条件的数量
	int u = s, v = mid + 1, t = s;
	while(u <= mid and v <= e){
		if(cmp3(B[u].z, B[v].z)){
			cnt += B[u].flag;
			C[t++] = B[u++];
		}else{ // 否时才统计, 说明u前面的点有符合条件的
            if(not B[v].flag){
				Ans[B[v].pos] += cnt; // 不能加u - s, 而要加u-s中y也符合条件的
			}
			C[t++] = B[v++];
		}        
	}
	while(u <= mid){
        C[t++] = B[u++];
	}
	while(v <= e){
		if(not B[v].flag){
			Ans[B[v].pos] += cnt;
		}
        C[t++] = B[v++];
	}
	copy(C.begin() + s, C.begin() + e + 1, B.begin() + s);
	return;
}

};

